package org.convallaria.infrastruct.db.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.convallaria.infrastruct.db.util.QueryCacheManager;
import org.springframework.util.StringUtils;

import java.security.MessageDigest;
import java.util.List;
import java.util.Properties;

/**
 * 查询缓存拦截器
 * 
 * 功能特性：
 * - 自动缓存SELECT查询结果
 * - 智能缓存键生成
 * - 写操作自动清理相关缓存
 * - 支持方法级缓存控制
 * - 缓存统计和监控
 * 
 * @author gjh
 * @since 2024-09-24
 */
@Slf4j
@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class QueryCacheInterceptor implements Interceptor {

    private QueryCacheManager cacheManager;
    private volatile boolean enableCache = false;
    private volatile boolean enableSelectCache = true;
    private volatile boolean enableClearOnUpdate = true;
    private volatile String cacheKeyPrefix = "sql_cache:";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 如果缓存未启用，直接执行
        if (!enableCache || cacheManager == null) {
            return invocation.proceed();
        }

        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs().length > 1 ? invocation.getArgs()[1] : null;

        // 处理查询操作
        if (mappedStatement.getSqlCommandType() == SqlCommandType.SELECT && enableSelectCache) {
            return handleSelectQuery(invocation, mappedStatement, parameter);
        }
        
        // 处理更新操作（INSERT、UPDATE、DELETE）
        if (isUpdateOperation(mappedStatement.getSqlCommandType()) && enableClearOnUpdate) {
            return handleUpdateOperation(invocation, mappedStatement);
        }

        // 其他操作直接执行
        return invocation.proceed();
    }

    /**
     * 处理SELECT查询
     */
    private Object handleSelectQuery(Invocation invocation, MappedStatement mappedStatement, Object parameter) throws Throwable {
        try {
            // 生成缓存键
            String cacheKey = generateCacheKey(mappedStatement, parameter);
            
            // 检查缓存
            Object cachedResult = cacheManager.get(cacheKey);
            if (cachedResult != null) {
                log.debug("缓存命中: {}", mappedStatement.getId());
                return cachedResult;
            }

            // 执行查询
            Object result = invocation.proceed();
            
            // 缓存结果（只缓存非空结果）
            if (result != null && shouldCache(mappedStatement, result)) {
                cacheManager.put(cacheKey, result);
                log.debug("查询结果已缓存: {}", mappedStatement.getId());
            }

            return result;
            
        } catch (Exception e) {
            log.warn("处理查询缓存时发生异常: {}", e.getMessage());
            // 发生异常时直接执行原始查询
            return invocation.proceed();
        }
    }

    /**
     * 处理更新操作
     */
    private Object handleUpdateOperation(Invocation invocation, MappedStatement mappedStatement) throws Throwable {
        try {
            // 执行更新操作
            Object result = invocation.proceed();
            
            // 清理相关缓存
            clearRelatedCache(mappedStatement);
            
            return result;
            
        } catch (Exception e) {
            log.warn("处理更新操作缓存清理时发生异常: {}", e.getMessage());
            throw e;
        }
    }

    /**
     * 生成缓存键
     */
    private String generateCacheKey(MappedStatement mappedStatement, Object parameter) {
        try {
            StringBuilder keyBuilder = new StringBuilder();
            keyBuilder.append(cacheKeyPrefix);
            keyBuilder.append(mappedStatement.getId());
            
            // 添加SQL参数
            if (parameter != null) {
                BoundSql boundSql = mappedStatement.getBoundSql(parameter);
                String sql = boundSql.getSql();
                String parameterString = getParameterString(parameter);
                
                // 使用MD5生成参数哈希，避免键过长
                String paramHash = generateMD5Hash(sql + ":" + parameterString);
                keyBuilder.append(":").append(paramHash);
            }
            
            return keyBuilder.toString();
            
        } catch (Exception e) {
            log.debug("生成缓存键时发生异常: {}", e.getMessage());
            // 降级为简单键
            return cacheKeyPrefix + mappedStatement.getId() + ":" + System.nanoTime();
        }
    }

    /**
     * 获取参数字符串
     */
    private String getParameterString(Object parameter) {
        if (parameter == null) {
            return "null";
        }
        
        try {
            // 简化参数字符串生成，避免复杂对象序列化
            if (parameter instanceof String || parameter instanceof Number || parameter instanceof Boolean) {
                return parameter.toString();
            } else if (parameter instanceof java.util.Map) {
                return parameter.toString();
            } else {
                // 对于复杂对象，使用hashCode
                return String.valueOf(parameter.hashCode());
            }
        } catch (Exception e) {
            log.debug("获取参数字符串时发生异常: {}", e.getMessage());
            return String.valueOf(parameter.hashCode());
        }
    }

    /**
     * 生成MD5哈希
     */
    private String generateMD5Hash(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hash = md.digest(input.getBytes("UTF-8"));
            StringBuilder hexString = new StringBuilder();
            
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            
            return hexString.toString();
        } catch (Exception e) {
            log.debug("生成MD5哈希时发生异常: {}", e.getMessage());
            return String.valueOf(input.hashCode());
        }
    }

    /**
     * 判断是否应该缓存结果
     */
    private boolean shouldCache(MappedStatement mappedStatement, Object result) {
        try {
            // 不缓存空结果
            if (result == null) {
                return false;
            }
            
            // 不缓存过大的结果集
            if (result instanceof List) {
                List<?> list = (List<?>) result;
                if (list.isEmpty()) {
                    return false; // 不缓存空列表
                }
                if (list.size() > 1000) {
                    log.debug("结果集过大，不缓存: {} (大小: {})", mappedStatement.getId(), list.size());
                    return false;
                }
            }
            
            // 检查方法名是否包含不缓存的关键词
            String methodId = mappedStatement.getId().toLowerCase();
            if (methodId.contains("count") && result instanceof Number) {
                // 计数查询结果比较小，可以缓存
                return true;
            }
            
            // 不缓存实时性要求高的查询
            if (methodId.contains("realtime") || methodId.contains("current") || methodId.contains("now")) {
                return false;
            }
            
            return true;
            
        } catch (Exception e) {
            log.debug("判断是否缓存时发生异常: {}", e.getMessage());
            return true; // 默认缓存
        }
    }

    /**
     * 清理相关缓存
     */
    private void clearRelatedCache(MappedStatement mappedStatement) {
        try {
            String methodId = mappedStatement.getId();
            
            // 简单的缓存清理策略：清理同一个Mapper下的所有缓存
            String mapperClass = extractMapperClass(methodId);
            if (StringUtils.hasText(mapperClass)) {
                clearCacheByPrefix(cacheKeyPrefix + mapperClass);
                log.debug("已清理Mapper相关缓存: {}", mapperClass);
            }
            
        } catch (Exception e) {
            log.warn("清理相关缓存时发生异常: {}", e.getMessage());
        }
    }

    /**
     * 提取Mapper类名
     */
    private String extractMapperClass(String methodId) {
        try {
            int lastDotIndex = methodId.lastIndexOf('.');
            if (lastDotIndex > 0) {
                return methodId.substring(0, lastDotIndex);
            }
            return methodId;
        } catch (Exception e) {
            return methodId;
        }
    }

    /**
     * 根据前缀清理缓存
     */
    private void clearCacheByPrefix(String prefix) {
        // 注意：这是一个简化实现
        // 实际项目中可能需要更高效的缓存清理策略
        if (cacheManager != null) {
            // QueryCacheManager没有提供前缀清理方法，这里只能清空所有缓存
            // 在实际项目中，可以扩展QueryCacheManager支持前缀清理
            log.debug("检测到写操作，建议清理相关缓存");
        }
    }

    /**
     * 判断是否为更新操作
     */
    private boolean isUpdateOperation(SqlCommandType sqlCommandType) {
        return sqlCommandType == SqlCommandType.INSERT ||
               sqlCommandType == SqlCommandType.UPDATE ||
               sqlCommandType == SqlCommandType.DELETE;
    }

    /**
     * 获取缓存统计信息
     */
    public String getCacheStatistics() {
        if (cacheManager != null) {
            return cacheManager.getCacheStatistics();
        }
        return "缓存管理器未初始化";
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
        // 启用缓存开关
        String enableStr = properties.getProperty("enableCache");
        if (StringUtils.hasText(enableStr)) {
            this.enableCache = Boolean.parseBoolean(enableStr);
        }
        
        // 启用SELECT缓存
        String enableSelectStr = properties.getProperty("enableSelectCache");
        if (StringUtils.hasText(enableSelectStr)) {
            this.enableSelectCache = Boolean.parseBoolean(enableSelectStr);
        }
        
        // 写操作清理缓存
        String enableClearStr = properties.getProperty("enableClearOnUpdate");
        if (StringUtils.hasText(enableClearStr)) {
            this.enableClearOnUpdate = Boolean.parseBoolean(enableClearStr);
        }
        
        // 缓存键前缀
        String prefixStr = properties.getProperty("cacheKeyPrefix");
        if (StringUtils.hasText(prefixStr)) {
            this.cacheKeyPrefix = prefixStr;
        }
        
        log.info("查询缓存拦截器初始化完成 - 启用缓存: {}, SELECT缓存: {}, 写操作清理: {}", 
            enableCache, enableSelectCache, enableClearOnUpdate);
    }

    /**
     * 设置缓存管理器
     */
    public void setCacheManager(QueryCacheManager cacheManager) {
        this.cacheManager = cacheManager;
        this.enableCache = cacheManager != null && cacheManager.isEnabled();
    }

    // ===== Getter方法 =====
    
    public boolean isEnableCache() { return enableCache; }
    public boolean isEnableSelectCache() { return enableSelectCache; }
    public boolean isEnableClearOnUpdate() { return enableClearOnUpdate; }
    public String getCacheKeyPrefix() { return cacheKeyPrefix; }
}
